package gu.sql2java.excel.json;

import com.google.common.base.Optional;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableMap;

import gu.sql2java.excel.annotations.ExcelHandlerAdapter;
import static com.google.common.base.Strings.isNullOrEmpty;
import static com.gitee.l0km.com4j.basex.json.JsonSupports.jsonSupportInstance;
import static com.google.common.base.Preconditions.checkNotNull;

/**
 * JSON类型字段使用自定义反序列化器实现的数据导入适配器抽象实现
 * @author guyadong
 * @since 3.29.0
 */
public abstract class BaseJsonDeserializeHandler<DES> implements ExcelHandlerAdapter {
	protected final DES initDeserializer;
	@SuppressWarnings("rawtypes")
	protected final Class initType;
	protected final Object initEmptyValue;
	private volatile Optional<DES> deserializer;
	@SuppressWarnings("rawtypes")
	private volatile Optional<Class> type;
	private volatile Optional<Object> emptyValue;
	public BaseJsonDeserializeHandler() {
		this.initDeserializer = null;
		this.initType = null;
		this.initEmptyValue=null;
	}
	public <T>BaseJsonDeserializeHandler(DES initDeserializer, Class<T> initType, T initEmptyValue) {
		this.initDeserializer = checkNotNull(initDeserializer,"initDeserializer is null");
		this.initType = checkNotNull(initType,"initType is null");
		this.initEmptyValue=initEmptyValue;
	}

	@SuppressWarnings("rawtypes")
	private final static ImmutableMap<String, Class> PRIMITIVES_MAP = ImmutableMap.<String, Class>builder()
			.put("boolean", Boolean.TYPE)
			.put("byte", Byte.TYPE)
			.put("char", Character.TYPE)
			.put("double", Double.TYPE)
			.put("float", Float.TYPE)
			.put("int", Integer.TYPE)
			.put("long", Long.TYPE)
			.put("short", Short.TYPE)
			.put("void", Void.TYPE)
			.build();

	@SuppressWarnings("rawtypes")
	private static Class classForName(String c) {
		try {
			Class clazz = PRIMITIVES_MAP.get(c);
			// 如果是引用类型，直接使用Class.forName方法
			return null == clazz ? Class.forName(c) : clazz;
		} catch (Exception e) {
			Throwables.throwIfUnchecked(e);
			throw new RuntimeException(e);
		} 
	}
	@Override
	public Object format(Object value, String[] args) {
		return value;
	}

	@Override
	public Object unformat(Object value, String[] args) {
		if(value instanceof String) {
			String s = (String)value;
			if(s.isEmpty()) {
				return getValueOfEmpty(args).orNull();
			}
			DES deserializer = getDeserializer(args).get();
			return deserialize(s,deserializer,getType(args).get());
		}else if(null == value) {
			return getValueOfEmpty(args).orNull();
		}
		return null;
	}
	
	@SuppressWarnings({ "rawtypes" })
	protected abstract Object deserialize(String value,DES deserializer,Class type) ;
	
	@SuppressWarnings("unchecked")
	private Optional<DES> getDeserializer(String[]args) {
		if(null == deserializer){
			synchronized (this) {
				if(null==deserializer){
					if(initDeserializer != null) {
						deserializer =  Optional.of(initDeserializer);
					}else if(args.length>0 && !isNullOrEmpty(args[0])) {
						try {
							deserializer = Optional.of((DES)Class.forName(args[0]).newInstance());
						} catch (Exception e) {
							throw new IllegalArgumentException("INVALUD deserializer type:"+args[0],e);
						}
					}else {
						throw new IllegalArgumentException("NOT DEFINE deserializer type IN args[0]");
					}
				}
			}
		}
		return deserializer;
	}
	@SuppressWarnings("rawtypes")
	private Optional<Class> getType(String[]args) {
		if(type == null) {
			synchronized (this) {
				if(type == null) {
					if(initType != null) {
						type = Optional.of(initType);
					}else if(args.length>1 && !isNullOrEmpty(args[1])) {
						try {
							type = Optional.of(classForName(args[1]));
						} catch (Exception e) {
							throw new IllegalArgumentException("INVALUD deserializer type:"+args[1],e);
						}
					}else{
						throw new IllegalArgumentException("NOT DEFINE type IN args[1]");
					}
				}
			}
		}
		return type;
	}
	@SuppressWarnings("unchecked")
	private Optional<Object> getValueOfEmpty(String[]args) {
		if(emptyValue == null) {
			synchronized (this) {
				if(emptyValue == null) {
					if(initEmptyValue != null) {
						emptyValue = Optional.of(initEmptyValue);
					}else if(args.length>2 && !isNullOrEmpty(args[2])) {
						try {
							Object parsed = jsonSupportInstance().parse(args[2], getType(args).get());
							emptyValue = Optional.of(parsed);
						} catch (Exception e) {
							throw new IllegalArgumentException("INVALUD empty value:"+args[2],e);
						}
					}else {
						emptyValue = Optional.absent();
					}
				}
			}
		}
		return emptyValue;
	}
}
